package com.h2cloud.aspect.aopInvokeProcess;

/**
 * @author: wulongkai
 * @create: 2022-10-10 10:49
 **/

import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;

/**
 * @author Andya
 * @create 2020-04-12 10:39
 */
//@Aspect
//@Component
@Slf4j
public class WebLogAspect {

    ThreadLocal<Long> startTime = new ThreadLocal<>();

    /**
     * 定义切入点，以controller下所有包的请求为切入点
     */
    @Pointcut("execution(public * com.h2cloud.controller..*.*(..))*")
    public void webLog() {

    }

    /**
     * 前置通知：在切入点之前执行的通知
     *
     * @param joinPoint
     * @throws Throwable
     */
    @Before("webLog()")
    public void doBefore(JoinPoint joinPoint) throws Throwable {
        log.info("========================================== doBefore Start ==========================================");
        // 方法签名
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        log.info("methodSignature.toString() = " + methodSignature.toString());
        log.info("methodSignature.toShortString() = " + methodSignature.toShortString());
        log.info("methodSignature.toLongString() = " + methodSignature.toLongString());
        log.info("methodSignature.getName() = " + methodSignature.getName());
        log.info("methodSignature.getModifiers() = " + methodSignature.getModifiers());
        // 方法所在的类
        log.info("methodSignature.getDeclaringType() = " + methodSignature.getDeclaringType());
        log.info("methodSignature.getDeclaringType().getSimpleName() = " + methodSignature.getDeclaringType().getSimpleName());
        log.info("methodSignature.getDeclaringType().getName() = " + methodSignature.getDeclaringType().getName());
        log.info("methodSignature.getDeclaringTypeName() = " + methodSignature.getDeclaringTypeName());

        //获取传入目标方法的参数
        log.info("传入目标方法的参数:");
        // 参数名
        String[] argNames = methodSignature.getParameterNames();
        // 参数值
        Object[] argValues = joinPoint.getArgs();
        for (int i = 0; i < argValues.length; i++) {
            log.info("    {}: {}={}", (i + 1), argNames[i], argValues[i]);
        }
        log.info("被代理的对象:" + joinPoint.getTarget());
        log.info("代理对象自己:" + joinPoint.getThis());


        startTime.set(System.currentTimeMillis());

        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = servletRequestAttributes.getRequest();

        //打印请求相关参数
        log.info("URL：" + request.getRequestURL().toString());
        log.info("IP：" + request.getRemoteAddr());
        log.info("HTTP_METHOD：" + request.getMethod());

        //header第一种格式展示
        Enumeration<String> enumeration = request.getHeaderNames();
        Map<String, String> headerMap = new HashMap<>();
        while (enumeration.hasMoreElements()) {
            String headerName = enumeration.nextElement();
            headerMap.put(headerName, request.getHeader(headerName));
        }
        String headerJsonStr = JSON.toJSONString(headerMap);
        if (headerJsonStr.length() > 0) {
            log.info("HTTP_HEADERS INFO IS: {}", headerJsonStr);
        }

        //header第二种格式展示
        log.info("HTTP_HEADERS: ");
        Enumeration<?> enumeration1 = request.getHeaderNames();
        while (enumeration1.hasMoreElements()) {
            String key = (String) enumeration1.nextElement();
            String value = request.getHeader(key);
            log.info("     {}: {}", key, value);
        }

        log.info("CLASS_METHOD：" + methodSignature.getDeclaringTypeName() + "." + methodSignature.getName());
        try {
            log.info("REQUEST BODY : [{}]", JSON.toJSONString(joinPoint.getArgs()[0]));
            log.info("ARGS：{}", Arrays.toString(joinPoint.getArgs()));
        } catch (Exception e) {
            log.error("REQUEST BODY PARSE ERROR!");
        }

        HttpSession session = (HttpSession) servletRequestAttributes.resolveReference(RequestAttributes.REFERENCE_SESSION);
        log.info("SESSION ID：" + session.getId());
        log.info("========================================== doBefore end ==========================================");
    }


    /**
     * 后置通知
     *
     * @param ret
     * @throws Throwable
     */
    @AfterReturning(returning = "ret", pointcut = "webLog()")
    public void doAfterReturning(Object ret) throws Throwable {
        log.info("========================================== doAfterReturning Start ==========================================");
        // 处理完请求，返回内容
        log.info("RESPONSE : " + ret);
        log.info("SPEND TIME : " + (System.currentTimeMillis() - startTime.get()));
        log.info("========================================== doAfterReturning end ==========================================");
    }


    /**
     * 后置最终通知
     *
     * @throws Throwable
     */
    @After("webLog()")
    public void doAfter() throws Throwable {
        log.info("========================================== doAfter Start ==========================================");
        // 每个请求之间空一行
        log.info("");
        log.info("========================================== doAfter end ==========================================");
    }


    /**
     * 环绕通知
     * 环绕通知第一个参数必须是org.aspectj.lang.ProceedingJoinPoint类型
     *
     * @param proceedingJoinPoint
     * @return
     * @throws Throwable
     */
    @Around("webLog()")
    public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        log.info("方法环绕 start...around");
        long startTime = System.currentTimeMillis();
        Object result = proceedingJoinPoint.proceed();
        String resultStr = JSON.toJSONString(result);
        // 打印出参
        log.info("RESPONSE ARGS  : {}", resultStr);
        // 执行耗时
        log.info("TIME-CONSUMING : {} ms", System.currentTimeMillis() - startTime);
        log.info("方法环绕 end...around");
        return "hello";
    }

}


